package com.lianqi.emcpframework.design.domain;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.lianqi.emcpframework.core.generator.IDGenerator;

/**
 * @program emcp-framework
 * @ClassName TreeEntity
 * @description: 树机构实体
 * @author: sky
 * @create: 2019/06/04 11:16
 */
public interface TreeEntity<T, ID> extends Entity<T, ID> {
    /**
     * 树状结构路径 分割符
     */
    static final String PATH_DIVIDER_TOKEN = "/";

    /**
     * 设置ID
     * 
     * @param id
     */
    void setId(ID id);

    /**
     * 获取路径
     * 
     * @return
     */
    String getPath();

    /**
     * 设置路径
     * 
     * @param path
     */
    void setPath(String path);

    /**
     * 获取父ID
     * 
     * @return
     */
    ID getParentId();

    /**
     * 设置父ID
     * 
     * @param parentId
     */
    void setParentId(ID parentId);

    /**
     * 获取层级
     * 
     * @return
     */
    Integer getLevel();

    /**
     * 设置层级
     * 
     * @param level
     */
    void setLevel(Integer level);

    /**
     * 获取子节点集合
     * 
     * @param <T>
     * @return
     */
    <T extends TreeEntity> List<T> getChildren();

    /**
     * 设置子节点集合
     *
     * @param
     * @return
     */
    void setChildren(List<T> children);

    /**
     * 迭代方法定义
     * 
     * @param list
     * @param consumer
     * @param <T>
     */
    static <T extends TreeEntity> void forEach(Collection<T> list, Consumer<T> consumer) {
        list.forEach(node -> {
            consumer.accept(node);
            if (node.getChildren() != null) {
                forEach(node.getChildren(), consumer);
            }
        });
    }

    /**
     * 树结构转换为数组结构 如未生成ID 、path、sort等填充对应数据
     * 
     * @param parent
     *            父节点
     * @param target
     *            目标数组
     * @param idGenerator
     *            id生成器
     * @param <T>
     *            元素类型
     * @param <ID>
     *            主键类型
     */
    static <T extends TreeEntity<T, ID>, ID> void treeToList(T parent, List<T> target, IDGenerator<ID> idGenerator) {
        treeToList(parent, target, idGenerator, null);
    }

    /**
     * 树结构转换为数组结构 如未生成ID 、path、sort等填充对应数据
     * 
     * @param parent
     *            父节点
     * @param target
     *            目标数组
     * @param idGenerator
     *            id生成器
     * @param childConsumer
     *            子节点消费接口,用于设置子节点
     * @param <T>
     *            元素类型
     * @param <ID>
     *            主键类型
     */
    static <T extends TreeEntity<T, ID>, ID> void treeToList(T parent, List<T> target, IDGenerator<ID> idGenerator,
        BiConsumer<T, List<T>> childConsumer) {
        List<T> children = parent.getChildren();
        if (childConsumer != null) {
            childConsumer.accept(parent, new ArrayList<>());
        }
        target.add(parent);
        if (parent.getId() == null) {
            parent.setId(idGenerator.generate());
        }
        if (parent.getPath() == null) {
            parent.setPath(providePath(parent));
        }
        parent.setLevel(getLevel(parent));
        if (parent instanceof SortableEntity) {
            Long sortValue = ((SortableEntity)parent).getSortValue();
            if (null == sortValue) {
                ((SortableEntity)parent).setSortValue(1L);
            }
        }
        if (children != null) {
            ID pid = parent.getId();
            for (int i = 0; i < children.size(); i++) {
                T child = children.get(i);
                if (child.getId() == null) {
                    child.setId(idGenerator.generate());
                }
                if (child instanceof SortableEntity && parent instanceof SortableEntity) {
                    if (((SortableEntity)child).getSortValue() == null) {
                        Long childSort = provideChildSort((SortableEntity)parent, i);
                        ((SortableEntity)child).setSortValue(childSort);
                    }
                }
                child.setParentId(pid);
                child.setPath(providePath(child));
                child.setLevel(getLevel(child));
                treeToList(child, target, idGenerator, childConsumer);
            }
        }
    }

    /**
     * 设置子节点 排序值
     * 
     * @param parent
     * @param idx
     * @param <T>
     * @param <ID>
     * @return
     */
    static <T extends TreeEntity<T, ID>, ID> Long provideChildSort(SortableEntity parent, int idx) {
        Long parentSort = parent.getSortValue();
        return new BigDecimal(parentSort + "0" + (idx + 1)).longValue();
    }

    /**
     * 获取层级
     * 
     * @param node
     * @param <T>
     * @param <ID>
     * @return
     */
    static <T extends TreeEntity<T, ID>, ID> int getLevel(T node) {
        return node.getPath().split(PATH_DIVIDER_TOKEN).length - 1;
    }

    /**
     * 设置路径
     * 
     * @param parent
     * @param <T>
     * @return
     */
    static <T extends TreeEntity> String providePath(T parent) {
        String path = "";
        if (parent.getParentId() == null) {
            path = PATH_DIVIDER_TOKEN + parent.getId();
        } else {
            path = parent.getPath() + PATH_DIVIDER_TOKEN + parent.getId();
        }
        return path;
    }

    /**
     * 列表结构转换为树结构 返回根节点集合
     *
     * @param dataList
     *            数据集合
     * @param <T>
     *            元素类型
     * @param <ID>
     *            主键类型
     * @return
     */
    static <T extends TreeEntity<T, ID>, ID> List<T> listToTree(Collection<T> dataList) {

        return listToTree(dataList, (node, childList) -> node.setChildren(childList));
    }

    /**
     * 列表结构转换为树结构 返回根节点集合
     * 
     * @param dataList
     *            数据集合
     * @param childConsumer
     *            子节点消费接口 用于设置子节点
     * @param <T>
     *            元素类型
     * @param <ID>
     *            主键类型
     * @return
     */
    static <T extends TreeEntity<T, ID>, ID> List<T> listToTree(Collection<T> dataList,
        BiConsumer<T, List<T>> childConsumer) {

        return listToTree(dataList, childConsumer, (Function<TreeHelper<T, ID>, Predicate<
            T>>)predicate -> node -> node.getParentId() == null || predicate.getNode(node.getParentId()) == null);
    }

    /**
     *
     * 列表结构转换为树结构 返回根节点集合
     * 
     * @param dataList
     *            数据集合
     * @param childConsumer
     *            子节点消费接口 用于设置子节点
     * @param rootNodePredicate
     *            根节点判断函数
     * @param <T>
     *            元素类型
     * @param <ID>
     *            主键类型
     * @return 根节点集合
     * @return
     */
    static <T extends TreeEntity<T, ID>, ID> List<T> listToTree(Collection<T> dataList,
        BiConsumer<T, List<T>> childConsumer, Predicate<T> rootNodePredicate) {
        return listToTree(dataList, childConsumer,
            (Function<TreeHelper<T, ID>, Predicate<T>>)predicate -> rootNodePredicate);
    }

    /**
     * 列表结构转换为树结构 返回根节点集合
     * 
     * @param dataList
     *            数据集合
     * @param childConsumer
     *            子节点消费接口 用于设置子节点
     * @param predicateFunction
     *            根节点判断函数
     * @param <T>
     *            元素类型
     * @param <ID>
     *            主键类型
     * @return 根节点集合
     */
    static <T extends TreeEntity<T, ID>, ID> List<T> listToTree(final Collection<T> dataList,
        final BiConsumer<T, List<T>> childConsumer, final Function<TreeHelper<T, ID>, Predicate<T>> predicateFunction) {

        Objects.requireNonNull(dataList, "source list can not be null!");
        Objects.requireNonNull(childConsumer, "child consumer can not be null!");
        Objects.requireNonNull(predicateFunction, "root predicate function can not be null!");

        Supplier<Stream<T>> streamSupplier =
            () -> dataList.size() < 1000 ? dataList.stream() : dataList.parallelStream();
        // id node 缓存
        Map<ID, T> nodeCache = new ConcurrentHashMap<>();
        // pid children 缓存
        Map<ID, List<T>> treeCache = streamSupplier.get().peek(node -> nodeCache.put(node.getId(), node))
            .collect(Collectors.groupingBy(TreeEntity::getParentId));

        Predicate<T> rootPredicate = predicateFunction.apply(new TreeHelper<T, ID>() {
            @Override
            public List<T> getChildren(ID parentId) {
                return treeCache.get(parentId);
            }

            @Override
            public T getNode(ID id) {
                return nodeCache.get(id);
            }
        });
        return streamSupplier.get()
            // 设置每个节点的子节点
            .peek(node -> childConsumer.accept(node, treeCache.get(node.getId())))
            // 获取根节点
            .filter(rootPredicate).collect(Collectors.toList());
    }

    /**
     * 树结构Helper
     *
     * @param <N>
     *            节点类型
     * @param <PK>
     *            主键类型
     */
    interface TreeHelper<N, PK> {
        /**
         * 根据主键获取子节点
         *
         * @param parentId
         *            节点ID
         * @return 子节点集合
         */
        List<N> getChildren(PK parentId);

        /**
         * 根据id获取节点
         *
         * @param id
         *            节点ID
         * @return 节点
         */
        N getNode(PK id);
    }

}
